package com.example.smarqueeview

import android.app.Activity
import android.view.LayoutInflater
import android.view.View
import androidx.fragment.app.Fragment
import androidx.viewbinding.ViewBinding
import java.lang.reflect.InvocationTargetException
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

/**
 * @author
 * @date 2021/2/5.
 * description：
 */
object ViewBindingHelper {
    /**
     * 给Activity自动绑定ViewBinding，通过inflate方式
     *
     * @param activity
     * @param <VB>
     * @return
    </VB> */
    fun <VB : ViewBinding?> injectViewBinding(activity: Activity): VB? {
        val type = activity.javaClass.genericSuperclass
        val viewBinding: VB = getViewBindingByInflate(type, activity.layoutInflater)!!
        if (viewBinding != null) {
            activity.setContentView(viewBinding.root)
        }
        return viewBinding
    }

    /**
     * 通过对root view进行绑定到ViewBinding，然后设置Activity内容
     *
     * @param activity
     * @param root
     * @param <VB>
     * @return
    </VB> */
    fun <VB : ViewBinding?> injectViewBinding(activity: Activity, root: View): VB? {
        val type = activity.javaClass.genericSuperclass
        val viewBinding: VB = getViewBindingByView(type, root)!!
        if (viewBinding != null) {
            activity.setContentView(viewBinding.root)
        } else {
            activity.setContentView(root)
        }
        return viewBinding
    }

    /**
     * 通过fragment以及泛型类型，获取viewBinding
     *
     * @param fragment
     * @param root
     * @param <VB>
     * @return
    </VB> */
    fun <VB : ViewBinding?> injectViewBinding(fragment: Fragment, root: View): VB {
        val type = fragment.javaClass.genericSuperclass
        return getViewBindingByView(type, root)!!
    }

    /**
     * 通过type泛型类获取ViewBinding，通过反射bind方法
     *
     * @param type
     * @param root
     * @param <VB>
     * @return
    </VB> */
    private fun <VB : ViewBinding?> getViewBindingByView(type: Type?, root: View): VB? {
        var viewBinding: VB? = null
        if (type is ParameterizedType) {
            val typeArguments = type.actualTypeArguments
            var viewBindingType: Type? = null
            for (argumentType in typeArguments) {
                if (ViewBinding::class.java.isAssignableFrom((argumentType as Class<*>))) {
                    viewBindingType = argumentType
                    break
                }
            }
            if (viewBindingType != null) {
                try {
                    val inflate =
                        (viewBindingType as Class<*>).getDeclaredMethod("bind", View::class.java)
                    viewBinding = inflate.invoke(null, root) as VB
                } catch (e: NoSuchMethodException) {
                    e.printStackTrace()
                } catch (e: IllegalAccessException) {
                    e.printStackTrace()
                } catch (e: InvocationTargetException) {
                    e.printStackTrace()
                }
            }
        }
        return viewBinding
    }

    /**
     * 通过type泛型类获取ViewBinding，通过反射inflate方法
     *
     * @param type
     * @param inflater
     * @param <VB>
     * @return
    </VB> */
    private fun <VB : ViewBinding?> getViewBindingByInflate(type: Type?,inflater: LayoutInflater): VB? {
        var viewBinding: VB? = null
        if (type is ParameterizedType) {
            val typeArguments = type.actualTypeArguments
            var viewBindingType: Type? = null
            for (argumentType in typeArguments) {
                if (ViewBinding::class.java.isAssignableFrom((argumentType as Class<*>))) {
                    viewBindingType = argumentType
                    break
                }
            }
            if (viewBindingType != null) {
                try {
                    val inflate = (viewBindingType as Class<*>).getDeclaredMethod(
                        "inflate",
                        LayoutInflater::class.java
                    )
                    viewBinding = inflate.invoke(null, inflater) as VB
                } catch (e: NoSuchMethodException) {
                    e.printStackTrace()
                } catch (e: IllegalAccessException) {
                    e.printStackTrace()
                } catch (e: InvocationTargetException) {
                    e.printStackTrace()
                }
            }
        }
        return viewBinding
    }
}